package com.maozzi.web.auth;

import cn.hutool.core.util.ObjectUtil;
import com.maozzi.api.acl.AclService;
import com.maozzi.api.acl.model.AclUserDTO;
import com.maozzi.api.user.UserService;
import com.maozzi.api.user.enums.UserTypeEnum;
import com.maozzi.api.user.model.UserDTO;
import com.maozzi.common.enums.BizCodeEnum;
import com.maozzi.common.exception.BizException;
import com.maozzi.common.util.CloneUtil;
import com.maozzi.common.util.TokenUtil;
import com.maozzi.web.auth.annotation.RequirePermissions;
import com.maozzi.web.auth.constant.AuthConstant;
import com.maozzi.web.auth.model.UserInfo;
import com.maozzi.web.auth.util.SsoUtil;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 权限拦截器
 *
 * @author maozi
 */
@Slf4j
@Component
public class AuthInterceptor implements HandlerInterceptor {
    @Resource
    private AclService aclService;
    @Resource
    private UserService userService;

    @Override
    public boolean preHandle(HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler) {
        String method = request.getMethod();
        if (HttpMethod.OPTIONS.matches(method)) {
            return true;
        }

        String url = request.getRequestURI();
        if (AuthConstant.ERROR_URLS.contains(url)) {
            return true;
        }

        String token = this.getToken(request);
        if (ObjectUtil.isEmpty(token)) {
            // 未登录
            throw new BizException(BizCodeEnum.NEED_LOGIN);
        }

        Boolean hasPermission = this.checkUserHasPermission(token, handler);
        if (!hasPermission) {
            throw new BizException(BizCodeEnum.FORBIDDEN);
        }

        return true;
    }

    /**
     * 获取cookie token
     *
     * @param request 请求
     * @return 返回
     */
    private String getToken(HttpServletRequest request) {
        String token = request.getHeader(HttpHeaders.AUTHORIZATION);
        if (Objects.isNull(token) || token.isBlank()) {
            return null;
        }

        return token.replace("Bearer", "").replaceAll(" ", "");
    }

    /**
     * 检查用户是否拥有权限
     *
     * @param token 用户token
     * @return false - 无权限
     */
    private Boolean checkUserHasPermission(String token, Object handler) {
        RequirePermissions annotation = null;
        // 如果是SpringMVC请求
        if (handler instanceof HandlerMethod handlerMethod) {
            annotation = handlerMethod.getMethodAnnotation(RequirePermissions.class);
            if (ObjectUtil.isEmpty(annotation)) {
                annotation = handlerMethod.getBeanType().getAnnotation(RequirePermissions.class);
            }
        }
        if (ObjectUtil.isEmpty(annotation)) {
            annotation = handler.getClass().getAnnotation(RequirePermissions.class);
        }
        if (Objects.isNull(annotation)) {
            // 没有加 RequirePermissions 注解，则不需要校验
            return true;
        }

        // 1. 解析 token
        Long userId = TokenUtil.decodeToken(token);

        // 2. 校验用户基本信息
        UserDTO userDTO = userService.getUser(userId);
        if (Objects.isNull(userDTO) || Objects.isNull(userDTO.getId())) {
            log.error("AuthInterceptor 鉴权失败，原因：id={}用户基本信息查询不到", userId);

            return false;
        }

        // 3. 校验用户权限
        AclUserDTO aclUserDTO = aclService.getAclUser(userId);
        if (Objects.isNull(aclUserDTO) || Objects.isNull(aclUserDTO.getId())) {
            log.error("AuthInterceptor 鉴权失败，原因：id={}用户权限信息查询不到", userId);

            return false;
        }

        UserInfo userInfo = CloneUtil.to(aclUserDTO, UserInfo.class);
        // 缓存登录态
        SsoUtil.set(userInfo);

        // 4. 检查用户类型
        Byte type = userInfo.getType();
        if (UserTypeEnum.NORMAL.getCode().equals(type)) {
            return false;
        }
        if (UserTypeEnum.SUPER_ADMIN.getCode().equals(type)) {
            return true;
        }

        // 5. 检查角色
        String[] roles = annotation.role();
        List<String> roleList = userInfo.getRoles();
        if (ObjectUtil.isNotEmpty(roles) && ObjectUtil.isNotEmpty(roleList)) {
            List<String> list = Arrays.stream(roles).filter(roleList::contains).collect(Collectors.toList());
            return ObjectUtil.isNotEmpty(list);
        }

        // 6. 检查权限码
        String[] codes = annotation.code();
        List<String> permissions = userInfo.getPermissions();
        if (ObjectUtil.isNotEmpty(codes) && ObjectUtil.isNotEmpty(permissions)) {
            List<String> list = Arrays.stream(codes).filter(permissions::contains).collect(Collectors.toList());
            return ObjectUtil.isNotEmpty(list);
        }

        return false;
    }

}
